﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once
#include "GcmpUiDocumentImpl.h"

// from gnuf
#include "GnufStateGuard.h"
#include "GnufImpl.h"
#include "GnufTrash.h"

// from gcmp
#include "IUiDocument.h"
#include "WeakPtr.h"

#include <forward_list>

#ifdef _GNUF_WIP_REFACTOR_UIDOC_UIVIEW_LOGIC_

namespace gcmp {
    class IUiDocument;
    class IDocument;
    class IUiView;
    class IModelView;
    class UiDocumentEvent;
    class UIView;
    class UIDocumentManager;
    
    namespace impl { class GNUF_IMPL_CLASS(UIDocument); }

    class GCMP_UIDOCUMENTIMPL_EXPORT UIDocument 
        : public gcmp::IUiDocument
        , public gcmp::Trash
    {
        DEFINE_CAST_DERIVED(gcmp::UIDocument, gcmp::IUiDocument);
        GNUF_IMPL_USE(UIDocument);

    protected:
        explicit UIDocument(UIDocumentManager *, gcmp::IDocument *);

    public:
        virtual ~UIDocument();
        
        enum class EState : std::uint8_t {
            eUnknown    = 0,
            eOpened     = 2,
            eActive     = 3,
            eClosed     = 4
        };
        ///\return Return the state.
        UIDocument::EState state() const;

        ///\return Return the UIDocumentManager.
        const UIDocumentManager * manager() const;
        UIDocumentManager * manager();
        
        ///\return Return the active UIView.
        const UIView * activeUIView() const;
        UIView * activeUIView();

        ///\return Return the DB document.
        const gcmp::IDocument * document() const;
        gcmp::IDocument * document();

    protected:
        // Inherits from gcmp::Trash...
        virtual void onDiscarded() override;
        virtual bool onAllowToDelete() const override;
        
        // Give the derived an opportunity to make a response.
        friend class gcmp::impl::UIDocumentImpl;
        virtual void onPreClosed(bool wantToCloseDbDocument) = 0;

    public:
        // Inherits from gcmp::IUiDocument...
        virtual int GetId() const override final;
        virtual const gcmp::IDocument * GetDbDocument() const override final;
        virtual gcmp::IDocument * GetDbDocument() override final;
        virtual std::vector< const gcmp::IUiView * > GetAllUiViews() const override final;
        virtual std::vector< gcmp::IUiView * > GetAllUiViews() override final;
    };

}

namespace gcmp { namespace impl {
    class UIViewFamily;

    class GNUF_IMPL_CLASS(UIDocument) final
    {
        GNUF_IMPL_USED_BY(UIDocument);
    public:
        GNUF_IMPL_CLASS(UIDocument)(UIDocument * pItf, UIDocumentManager *, gcmp::IDocument *);
        ~GNUF_IMPL_CLASS(UIDocument)();

        int id() const { return m_runtimeId; }

        const UIView * uiViewById(const int id) const;
        UIView * uiViewById(const int id);

        bool activateUIViewInsteadOfActivatingMyself(const int uiViewId = -1 /* invalid */);
        
        UIDocument::EState state() const { return m_state(); }
        bool isSwitchingState(UIDocument::EState * pNewState = nullptr) const;
        bool isActive() const { return (UIDocument::EState::eActive == m_state()); }

        bool closeAllUIViews();
        
        bool close(bool wantToCloseDbDocument, const bool * pWasActive = nullptr);
        bool activate();
        bool deactivate();

        // Return the manager.
        const UIDocumentManager * manager() const;
        UIDocumentManager * manager();
        
        // Return the active UIView.
        const UIView * activeUIView() const;
        UIView * activeUIView();

        // Return the DB document.
        const gcmp::IDocument * document() const;
        gcmp::IDocument * document();

        // Return the family.
        const UIViewFamily * family() const;
        UIViewFamily * family();

        // Add an UIView.
        UIView * addUIView(bool isDrawing, gcmp::IModelView *, const std::wstring & uniqueTag);
        
        void travelRecursively(const std::function<bool(const UIView *)> & callback) const;
        void travelRecursively(const std::function<bool(UIView *)> & callback);
        
        // Clear the invalid UIView(s) whose model view(s) are null.
        // Note that, we should have depended on ModelViewEvent to do this, but
        // the DB layer cannot send out a believable event. This sounds odd.
        // -frank 2022.11.22
        void clearInvalidUIViews();
        
        void onCreated(UIView *);
        
        // For switching UIView's state.
        bool  onPreClosed       (const UIView *, const bool wasActive);
        void onPostClosed       (      UIView *, const bool refused = false);
        bool  onPreActivated    (const UIView *);
        void onPostActivated    (const UIView *);
        bool  onPreDeactivated  (const UIView *);
        void onPostDeactivated  (const UIView *);
        
        inline size_t numberOfAllUIViews() const;
        using UIViewWeakPtr = gcmp::WeakPtr< UIView >;
        using UIViews       = std::forward_list< UIViewWeakPtr >;
        using RawUIViews    = std::vector< UIView * >;
        const UIDocument::Impl::UIViews & allUIViews() const { return m_allUIViews; }
        UIDocument::Impl::RawUIViews rawAllUIViews();

        bool isClosingDocument() const { return m_closingDocument; }
        void setClosingDocument() { m_closingDocument = true; }
        
    private:
        // Remove the UIView.
        bool removeUIView(UIView *);
        bool onPreClosed();
        void onPostClosed(bool wantToCloseDbDocument);
        gcmp::UiDocumentEvent * evt();
        
        bool resetActiveUIViewDueTo(const UIView *);
        
        void doActivate();
        void doDeactivate();

        bool   cacheUIView      (const UIView *);
        bool uncacheUIView      (const UIView *);
        bool moveUIViewToFront  (const UIView *);

        int m_runtimeId = -1; // invalid, by default
        gcmp::WeakPtr< gcmp::IDocument > m_wpDoc;
        gcmp::StateGuard< UIDocument::EState > m_state;
        struct Active {
            friend bool UIDocument::Impl::onPreActivated(const UIView *);
            friend void UIDocument::Impl::onPostActivated(const UIView *);
            friend void UIDocument::Impl::onPostDeactivated(const UIView *);
            enum EState : std::uint8_t { eNull, ePending, eConfirmed };
            UIDocument::Impl::Active::EState operator()() const { return m_data; }
            bool isNull()      const { return (UIDocument::Impl::Active::eNull       == m_data); }
            bool isPending()   const { return (UIDocument::Impl::Active::ePending    == m_data); }
            bool isConfirmed() const { return (UIDocument::Impl::Active::eConfirmed  == m_data); }
        private:
            void operator=(const UIDocument::Impl::Active::EState val) { m_data = val; }
            UIDocument::Impl::Active::EState m_data = UIDocument::Impl::Active::eNull;
        } m_activeUIView;
        gcmp::OwnerPtr< UIViewFamily > m_opFamily;
        struct {
            friend void UIDocument::Impl::clearInvalidUIViews();
            bool operator()() const { return m_data; }
        private: bool m_data = false; } m_clearingInvalidUIViews;
        UIDocument::Impl::UIViews m_allUIViews;
        bool m_closingDocument = false; // Oops! Just follow the current logic. Don't know why.
    };
} }

#endif // !_GNUF_WIP_REFACTOR_UIDOC_UIVIEW_LOGIC_